package com.open.center.common.exception;

import java.util.stream.Collectors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;

import com.open.center.common.model.CommonResult;

/**
 * 全局异常处理
 *
 * @author Riche's
 * @since 2022/8/18
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * Exception 全局处理
     *
     * @param request 请求
     * @param e       异常类
     * @return CommonResult
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public CommonResult<String> exception(HttpServletRequest request, Exception e) {
        log.error("Exception", e);
        if (StringUtils.hasText(e.getMessage())) {
            log.error("Url = {}, Exception = {}, message = {}", request.getRequestURI(), e.getClass().getSimpleName(), e.getMessage());
            return CommonResult.fail(e.getMessage());
        }
        return CommonResult.fail(request.getRequestURI());
    }

    /**
     * 处理业务异常
     *
     * @param request 请求
     * @param e       异常
     * @return CommonResult
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(OpenException.class)
    public CommonResult<String> exception(HttpServletRequest request, OpenException e) {
        log.error("OpenException", e);
        if (StringUtils.hasText(e.getMessage())) {
            log.error("OpenException = {}, message = {}", e.getClass().getSimpleName(), e.getMessage());
            return CommonResult.fail(e.getMessage());
        }
        return CommonResult.fail(request.getRequestURI());
    }

    /**
     * 处理Hibernate Validation 验证失败的信息
     *
     * @param e 异常类
     * @return CommonResult
     */
    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public CommonResult<String> validationException(Exception e) {
        log.error("validationException = {}, message = {}", e.getClass().getSimpleName(), e.getMessage());
        CommonResult<String> result = CommonResult.fail(HttpStatus.PRECONDITION_FAILED.getReasonPhrase());
        if (e instanceof MethodArgumentNotValidException exception) {
            exception.getBindingResult().getAllErrors().forEach(err ->
                    log.error("MethodArgumentNotValidException: {}", err.getDefaultMessage())
            );
            result.setMessage(exception.getFieldErrors()
                    .stream()
                    .map(ObjectError::getDefaultMessage)
                    .collect(Collectors.joining("; ")));
        } else if (e instanceof ConstraintViolationException exception) {
            exception.getConstraintViolations().forEach(v ->
                    log.error("ConstraintViolationException: {}", v.getMessage())
            );
            result.setMessage(exception.getConstraintViolations()
                    .stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining("; ")));
        } else if (e instanceof BindException exception) {
            exception.getAllErrors().forEach(err -> log.error("BindException: {}", err.getDefaultMessage()));
            result.setMessage(exception.getFieldErrors()
                    .stream()
                    .map(ObjectError::getDefaultMessage)
                    .collect(Collectors.joining("; ")));
        }
        return result;
    }

    /**
     * 上传异常
     *
     * @param e 异常
     * @return CommonResult
     */
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    @ExceptionHandler(value = {MultipartException.class, MaxUploadSizeExceededException.class})
    public CommonResult<String> uploadException(Exception e) {
        log.error("uploadException = {}, message = {}", e.getClass().getSimpleName(), e.getMessage());
        CommonResult<String> result = CommonResult.fail("文件上传失败");
        if (e instanceof MaxUploadSizeExceededException exception) {
            result.setMessage("上传文件的大小限制为1MB");
            result.setData(exception.getMessage());
        } else if (e instanceof MultipartException exception) {
            log.error("MultipartException {}", exception.getMessage());
        }
        return result;
    }

}
